package com.liuqianfei.markme.rules.block;

import com.liuqianfei.markme.core.Token;
import com.liuqianfei.markme.rules.AbstractState;
import com.liuqianfei.markme.rules.IAction;
import com.liuqianfei.util.StringUtil;

/**
 * fences (``` lang, ~~~ lang)
 * @author liuqianfei
 */
public class FenceAction implements IAction
{
    @Override
    public boolean parse(AbstractState state, boolean silent, int startLine, int endLine)
    {
        char marker;
        int len;
        String params;
        int nextLine;
        int mem;
        Token token;
        String markup;
        boolean haveEndMarker = false;
        int pos = state.getbMarks(startLine) + state.gettShift(startLine);
        int max = state.geteMarks(startLine);

        if (pos + 3 > max) { return false; }

        marker = state.getSrc().charAt(pos);

        if (marker != 0x7E/* ~ */ && marker != 0x60 /* ` */)
            return false;

        // scan marker length
        mem = pos;
        pos = state.skipChars(pos, marker);

        len = pos - mem;

        if (len < 3) { return false; }

        markup = StringUtil.slice(state.getSrc(), mem, pos);
        params = StringUtil.slice(state.getSrc(), pos, max);

        if (params.contains(String.valueOf(marker)))
            return false;

        // Since start is found, we can report success here in validation mode
        if (silent)
            return true;

        // search end of block
        nextLine = startLine;

        for (;;)
        {
            nextLine++;
            if (nextLine >= endLine)
            {
                // unclosed block should be autoclosed by end of document.
                // also block seems to be autoclosed by end of parent
                break;
            }

            pos = mem = state.getbMarks(nextLine) + state.gettShift(nextLine);
            max = state.geteMarks(nextLine);

            if (pos < max && state.getsCount(nextLine) < state.getBlkIndent())
            {
                // non-empty line with negative indent should stop the list:
                // - ```
                //  test
                break;
            }

            if (state.getSrc().charAt(pos) != marker)
                continue;

            if (state.getsCount(nextLine) - state.getBlkIndent() >= 4)
            {
                // closing fence should be indented less than 4 spaces
                continue;
            }

            pos = state.skipChars(pos, marker);

            // closing code fence must be at least as long as the opening one
            if (pos - mem < len)
                continue;

            // make sure tail has spaces only
            pos = state.skipSpaces(pos);

            if (pos < max)
                continue;

            haveEndMarker = true;
            // found!
            break;
        }

        // If a fence has heading spaces, they should be removed from its inner block
        len = state.getsCount(startLine);

        state.setLine(nextLine + (haveEndMarker ? 1 : 0));

        token         = state.push("fence", "code", 0);
        token.info    = params;
        token.content = state.getLines(startLine + 1, nextLine, len, true);
        token.markup  = markup;
        token.map     = new int[]{startLine, state.getLine()};

        return true;
    }
}
